/*
 * Created by SharpDevelop.
 * User: MyWife
 * Date: 13/09/08
 * Time: 8:30 PM
 * 
 * To change this template use Tools | Options | Coding | Edit Standard Headers.
 */

using System;
using System.Collections;
using System.Collections.Generic;
using System.Net.Mail;
using System.IO;
using System.Net;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;
using popLib.ExtensionClass;
using System.Text;


namespace popLib
{
    /// <summary>
    /// Description of NetLib.
    /// </summary>
    public class NetLib
    {
        public static string sendMail(string from, string to, string subject, string body, string server, int port, string username, string password)
        {
            return sendMail(from, to, null, null, subject, body, null, server, port, username, password, false);
        }

        public static string sendMail(string from, string to, string cc, string bcc, string subject, string body, string[] fileAttack, string server, int port, string username, string password, bool enableSsl)
        {
            // Mail initialization
            MailMessage mailMsg = new MailMessage();

            mailMsg.From = new MailAddress(from);
            if (StringLib.isValid(to)) foreach (string mail in StringLib.SplitByAny(to, ";,")) mailMsg.To.Add(mail);
            else return "Destination is not valid!";

            if (StringLib.isValid(cc)) foreach (string mail in StringLib.SplitByAny(cc, ";,")) mailMsg.CC.Add(mail);
            if (StringLib.isValid(bcc)) foreach (string mail in StringLib.SplitByAny(bcc, ";,")) mailMsg.Bcc.Add(mail);

            mailMsg.Subject = subject;
            mailMsg.BodyEncoding = System.Text.Encoding.Unicode;
            mailMsg.Body = body;
            mailMsg.Priority = MailPriority.High;
            mailMsg.BodyEncoding = System.Text.Encoding.UTF8;

            if (fileAttack != null && fileAttack.Length > 0)
                foreach (string tmpFile in fileAttack)
                    if (tmpFile.Trim().Length > 0 && File.Exists(tmpFile))
                        mailMsg.Attachments.Add(new Attachment(tmpFile));


            SmtpClient smtp = new SmtpClient(server, port);
            if (StringLib.isValid(username) && password != null)
            {
                System.Net.NetworkCredential credentials = new System.Net.NetworkCredential(username, password);
                smtp.Credentials = credentials;
            }

            smtp.EnableSsl = enableSsl;

            try
            {
                smtp.Send(mailMsg);
                return "";
            }
            catch (Exception ex)
            {
                return ex.Message;
            }
        }

        public static ReturnSet DownloadFile(string url, string localFile, NetworkSecurity netsec, int retryTimes)
        {
            int time = 0;
            ReturnSet result = null;

            while (time < retryTimes)
            {
                result = DownloadFile(url, localFile, netsec);
                time++;
                if (result.Success) break;
            }

            return result;
        }

        public static ReturnSet DownloadFile(string url, string localFile, NetworkSecurity netsec)
        {
            try
            {
                WebClient client = new WebClient();

                if (netsec != null && netsec.Proxy != null)
                {
                    client.Proxy = netsec.Proxy;
                }

                client.DownloadFile(url, localFile);
                return new ReturnSet(true, localFile);
            }
            catch (Exception ex)
            {
                return new ReturnSet(false,
                                     string.Format("URL: {0}\r\nLocalFile: {1}\r\n{2}", url, localFile, ex.ToString()));
            }
        }

        public static ReturnSet UploadFile(string localFile, string url)
        {
            return UploadFile(localFile, url, null, null);
        }

        public static ReturnSet UploadFile(string localFile, string url, Hashtable paramList)
        {
            return UploadFile(localFile, url, paramList, null);
        }

        public static ReturnSet UploadFile(string localFile, string url, Hashtable paramList, NetworkSecurity netsec)
        {
            try
            {
                WebClient client = new WebClient();

                if (netsec != null && netsec.Proxy != null)
                {
                    client.Proxy = netsec.Proxy;
                }

                if (paramList != null)
                {
                    foreach (string key in paramList.Keys)
                        client.Headers.Add(key, paramList[key].ToString());
                }
                byte[] result = client.UploadFile(url, localFile);

                return new ReturnSet(true, result, System.Text.Encoding.ASCII.GetString(result));
            }
            catch (Exception ex)
            {
                return new ReturnSet(false, ex, string.Format("URL: {0}\r\nLocalFile: {1}\r\n{2}", url, localFile, ex.ToString()));
            }
        }

        public static ReturnSet UploadFile(string localFile, string url, Hashtable paramList, NetworkSecurity netsec, int retryTimes)
        {
            int time = 0;
            ReturnSet result = null;

            while (time < retryTimes)
            {
                result = UploadFile(localFile, url, paramList, netsec);
                time++;
                if (result.Success) break;
            }

            return result;
        }

        
        #region ReturnSet counterTry(...)

        public static ReturnSet counterTry(DelegateSet.counterTryDelegate2 func, int retryTimes, string param1, string param2)
        {
            int time = 0;
            ReturnSet result = null;

            while (time < retryTimes)
            {
                result = func(param1, param2);
                time++;
                if (result.Success) break;
            }

            return result;
        }

        public static ReturnSet counterTry(DelegateSet.counterTryDelegate3 func, int retryTimes, string param1, string param2, string param3)
        {
            int time = 0;
            ReturnSet result = null;

            while (time < retryTimes)
            {
                result = func(param1, param2, param3);
                time++;
                if (result.Success) break;
            }

            return result;
        }


        public static ReturnSet counterTry(DelegateSet.counterTryDelegate4 func, int retryTimes, string param1, string param2, string param3, string param4)
        {
            int time = 0;
            ReturnSet result = null;

            while (time < retryTimes)
            {
                result = func(param1, param2, param3, param4);
                time++;
                if (result.Success) break;
            }

            return result;
        }


        public static ReturnSet counterTry(DelegateSet.counterTryDelegate5 func, int retryTimes, string param1, string param2, string param3, string param4, string param5)
        {
            int time = 0;
            ReturnSet result = null;

            while (time < retryTimes)
            {
                result = func(param1, param2, param3, param4, param5);
                time++;
                if (result.Success) break;
            }

            return result;
        }


        #endregion


        public static ReturnSet createImapServer(string host, string username, string password, bool useSSL)
        {
            string[] parts = host.Split(':');
            int port = (parts.Length == 2) ? int.Parse(parts[1]) : useSSL ? 993 : 143;
            ExtensionClass.Imap imap = new ExtensionClass.Imap(parts[0], port, useSSL);
            return new ReturnSet(imap.AuthenticateUser(username, password), imap);
        }


        /************************ HTTP REQUEST ******************************/
        static CookieCollection Cookies;

        #region basic https functions
        private static ReturnSet InitWebRequest(string url, NetworkSecurity netsec)
        {
            ReturnSet result = new ReturnSet(false, null);
            try
            {

                HttpWebRequest request = (HttpWebRequest)System.Net.WebRequest.Create(url);
                request.Method = "POST";
                request.KeepAlive = true;
                if (Cookies != null && Cookies.Count > 0) request.CookieContainer.Add(Cookies);
                //request.UserAgent 						= "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; .NET CLR 1.0.3705)";

                if (netsec != null && netsec.Certificate != null)
                {
                    request.ClientCertificates.Add(netsec.Certificate);
                    ServicePointManager.ServerCertificateValidationCallback += delegate(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
                    {
                        return true;
                    };
                }

                if (netsec != null && netsec.Proxy != null)
                {
                    request.Proxy = netsec.Proxy;
                }

                result = new ReturnSet(true, request);
            }
            catch (WebException ex)
            {
                result = new ReturnSet(false, ex, "ERROR_MESSAGE=HttpWebRequest error, can not connect to server (" + url + ")\r\n\r\n" + ex.ToString());
            }
            return result;
        }

        private static ReturnSet GetResponseStr(HttpWebRequest request)
        {
            ReturnSet result = new ReturnSet(false, null);
            try
            {
                HttpWebResponse response = (HttpWebResponse)request.GetResponse();
                StreamReader reader = new StreamReader(response.GetResponseStream());
                if (response.Cookies.Count > 0) Cookies = response.Cookies;
                string resultStr = reader.ReadToEnd();
                result = new ReturnSet(true, resultStr);
            }
            catch (WebException ex)
            {
                result = new ReturnSet(false, ex, "ERROR_MESSAGE=HttpWebResponse error, can not connect to server (" + request.Address.OriginalString + ")\r\n\r\n" + ex.ToString());
            }
            return result;
        }

        private static ReturnSet PostParameter(HttpWebRequest request, Hashtable paramList)
        {
            return PostParameter(request, paramList, new Hashtable());
        }

        private static ReturnSet PostParameter(HttpWebRequest request, Hashtable paramList, Hashtable fileList)
        {

            ReturnSet result = new ReturnSet(false);
            try
            {
                //init list of mimePart
                List<MimePart> mimeParts = new List<MimePart>();

                //add parameters
                foreach (string param in paramList.Keys)
                {
                    mimeParts.Add(new MimePart(param, paramList[param].ToString()));
                }

                //add files
                foreach (string fieldname in fileList.Keys)
                {
                    mimeParts.Add(new MimePart(fieldname, fileList[fieldname].ToString(), null));
                }

                //init boundary, footer and content length
                long contentLength = 0;
                string boundary = "----------" + DateTime.Now.Ticks.ToString("x");
                request.ContentType = "multipart/form-data; boundary=" + boundary;
                request.Method = "POST";

                byte[] _footer = Encoding.UTF8.GetBytes("--" + boundary + "--\r\n");

                foreach (MimePart part in mimeParts)
                {
                    contentLength += part.GenerateHeaderFooterData(boundary);
                }

                request.ContentLength = contentLength + _footer.Length;

                //write parameter and file to stream -> send to server
                byte[] buffer = new byte[8192];
                byte[] afterFile = Encoding.UTF8.GetBytes("\r\n");
                int read;

                using (Stream s = request.GetRequestStream())
                {
                    foreach (MimePart part in mimeParts)
                    {
                        s.Write(part.Header, 0, part.Header.Length);

                        while ((read = part.Data.Read(buffer, 0, buffer.Length)) > 0)
                            s.Write(buffer, 0, read);

                        part.Data.Dispose();

                        s.Write(afterFile, 0, afterFile.Length);
                    }

                    s.Write(_footer, 0, _footer.Length);
                }
                result = new ReturnSet(true);
            }
            catch (Exception ex)
            {
                result = new ReturnSet(false, ex, "ERROR_MESSAGE=Post parameter error, can not connect to server (" + request.Address.OriginalString + ")\r\n\r\n" + ex.ToString());
            }
            return result;
        }
        #endregion



        #region Connect(string url, string paramList)


        /// <summary>
        /// Connect and send param to server, get response web http
        /// </summary>
        /// <param name="url">URL connect to server</param>
        /// <param name="paramList">Hashtable with key is param name and value is param value</param>
        /// <param name="certificateFile">Certificate file path, null if not available</param>
        /// <param name="retryTimes">Total times you want to retry if execute function fail</param>
        /// <returns>
        ///  - Sucess:true, Result: response http string
        ///  - Sucess:false, Result: Exception object, Message: error message
        /// </returns>
        public static ReturnSet connectHttp(string url, Hashtable paramList, NetworkSecurity netsec, int retryTimes)
        {
            int time = 0;
            ReturnSet result = null;

            while (time < retryTimes)
            {
                result = connectHttp(url, paramList, netsec);
                time++;
                if (result.Success) break;
            }

            return result;
        }


        public static ReturnSet connectHttp(string url, Hashtable paramList)
        {
            return connectHttp(url, paramList, null);
        }
        /// <summary>
        /// Connect and send param to server, get response web http
        /// </summary>
        /// <param name="url">URL connect to server</param>
        /// <param name="paramList">Hashtable with key is param name and value is param value</param>
        /// <param name="certificateFile">Certificate file path, null if not available</param>
        /// <returns>
        ///  - Sucess:true, Result: response http string
        ///  - Sucess:false, Result: Exception object, Message: error message
        /// </returns>
        public static ReturnSet connectHttp(string url, Hashtable paramList, NetworkSecurity netsec)
        {
            HttpWebRequest request = null;
            ReturnSet result = new ReturnSet(false, null);
            try
            {
                ReturnSet requestResult = InitWebRequest(url, netsec);
                if (requestResult.Success) request = (HttpWebRequest)requestResult.Result;
                else return result;

                result = PostParameter(request, paramList);

                if (result.Success)
                    result = GetResponseStr(request);

            }
            catch (Exception ex)
            {
                result = new ReturnSet(false, ex, "ERROR_MESSAGE=Can not [connect] to url(" + url + ").\r\n\r\n" + ex.ToString());
            }
            return result;
        }
        #endregion

        #region UploadFile(string file, string url)
        public delegate ReturnSet uploadFileHttpDelegate(Hashtable fileList, string url, Hashtable paramList, NetworkSecurity netsec);

        /// <summary>
        /// Upload file to HTTPS server using POST method
        /// </summary>
        /// <param name="fileList">Hashtable to upload one or mult-files. key is fieldname, value is file path. Ex: file,C:\abc.exe</param>
        /// <param name="url">URL connect to server</param>
        /// <param name="paramList">Hashtable with key is param name and value is param value</param>
        /// <param name="certificateFile">Certificate file path, null if not available</param>
        /// <param name="retryTimes">Total times you want to retry if execute function fail</param>
        /// <returns>
        ///  - Sucess:true, Result: response http string
        ///  - Sucess:false, Result: Exception object, Message: error message
        /// </returns>
        public static ReturnSet UploadFileHttp(Hashtable fileList, string url, Hashtable paramList, NetworkSecurity netsec, int retryTimes)
        {
            int time = 0;
            ReturnSet result = null;

            while (time < retryTimes)
            {
                result = UploadFileHttp(fileList, url, paramList, netsec);
                time++;
                if (result.Success) break;
            }

            return result;
        }

        public static ReturnSet UploadFileHttp(Hashtable fileList, string url, Hashtable paramList)
        {
            return UploadFileHttp(fileList, url, paramList, null);
        }
        /// <summary>
        /// Upload file to HTTPS server using POST method
        /// </summary>
        /// <param name="fileList">Hashtable to upload one or mult-files. key is fieldname, value is file path. Ex: file,C:\abc.exe</param>
        /// <param name="url">URL connect to server</param>
        /// <param name="paramList">Hashtable with key is param name and value is param value</param>
        /// <param name="certificateFile">Certificate file path, null if not available</param>
        /// <returns>
        ///  - Sucess:true, Result: response http string
        ///  - Sucess:false, Result: Exception object, Message: error message
        /// </returns>
        public static ReturnSet UploadFileHttp(Hashtable fileList, string url, Hashtable paramList, NetworkSecurity netsec)
        {
            HttpWebRequest request = null;

            ReturnSet requestResult = InitWebRequest(url, netsec);
            if (requestResult.Success) request = (HttpWebRequest)requestResult.Result;
            else return requestResult;

            ReturnSet result = PostParameter(request, paramList, fileList);

            if (result.Success)
                result = GetResponseStr(request);

            return result;
        }
        #endregion

        #region DownloadFile( string url, string destFile )
        public delegate ReturnSet DownloadFileHttpDelegate(string url, string destFile, Hashtable paramList, string certificateFile);

        /// <summary>
        /// Download file and save to destination file
        /// </summary>
        /// <param name="url">URL of file in server</param>
        /// <param name="destFile">file path whese to save downloaded file</param>
        /// <param name="paramList">Hashtable with key is param name and value is param value</param>
        /// <param name="certificateFile">Certificate file path, null if not available</param>
        /// <param name="retryTimes">Total times you want to retry if execute function fail</param>
        /// <returns>
        ///  - Sucess:true, Result: null
        ///  - Sucess:false, Result: Exception object, Message: error message
        /// </returns>
        public static ReturnSet downloadFileHttp(string url, string destFile, Hashtable paramList, NetworkSecurity netsec, int retryTimes)
        {
            int time = 0;
            ReturnSet result = null;

            while (time < retryTimes)
            {
                result = downloadFileHttp(url, destFile, paramList, netsec);
                time++;
                if (result.Success) break;
            }

            return result;
        }

        public static ReturnSet downloadFileHttp(string url, string destFile, Hashtable paramList)
        {
            return downloadFileHttp(url, destFile, paramList, null);
        }

        /// <summary>
        /// Download file and save to destination file
        /// </summary>
        /// <param name="url">URL of file in server</param>
        /// <param name="destFile">file path whese to save downloaded file</param>
        /// <param name="paramList">Hashtable with key is param name and value is param value</param>
        /// <param name="certificateFile">Certificate file path, null if not available</param>
        /// <param name="retryTimes">Total times you want to retry if execute function fail</param>
        /// <returns>
        ///  - Sucess:true, Result: null
        ///  - Sucess:false, Result: Exception object, Message: error message
        /// </returns>
        public static ReturnSet downloadFileHttp(string url, string destFile, Hashtable paramList, NetworkSecurity netsec)
        {
            Stream readStream = null;
            FileStream writeStream = null;
            HttpWebRequest request = null;

            ReturnSet requestResult = InitWebRequest(url, netsec);
            if (requestResult.Success) request = (HttpWebRequest)requestResult.Result;
            else return requestResult;

            //post param
            ReturnSet result = PostParameter(request, paramList);
            if (!result.Success) return result;

            try
            {
                HttpWebResponse response = (HttpWebResponse)request.GetResponse();
                readStream = response.GetResponseStream();
                writeStream = new FileStream(destFile, FileMode.Create, FileAccess.Write);

                int Length = 256;
                Byte[] buffer = new Byte[Length];
                int bytesRead = readStream.Read(buffer, 0, Length);
                // write the required bytes
                while (bytesRead > 0)
                {
                    writeStream.Write(buffer, 0, bytesRead);
                    bytesRead = readStream.Read(buffer, 0, Length);
                }
                result = new ReturnSet(true, null);
            }
            catch (Exception ex)
            {
                result = new ReturnSet(false, ex, "ERROR_MESSAGE=Can not [download file] (url: " + url + ").Error::\r\n\r\n" + ex.ToString());
            }
            finally
            {
                if (readStream != null) readStream.Close();
                if (writeStream != null) writeStream.Close();
            }
            return result;
        }
        #endregion

        public static bool isConnectedToInternet()
        {
            int desc;
            return APILib.InternetGetConnectedState(out desc, 0);
        }

        public static string GetLocalIP()
        {
            string _IP = null;
            System.Net.IPHostEntry _IPHostEntry = System.Net.Dns.GetHostEntry(System.Net.Dns.GetHostName());
            foreach (System.Net.IPAddress _IPAddress in _IPHostEntry.AddressList)
                if (_IPAddress.AddressFamily.ToString() == "InterNetwork")
                    _IP = _IPAddress.ToString();
            return _IP;
        }
    }

    #region Classes
    public class MimePart
    {
        Hashtable _headers = new Hashtable();
        byte[] _header;
        Stream _data;

        public enum ContentType
        {
            application_octetstream = 0
        }

        public MimePart(string parameterName, string value)
        {
            _headers["Content-Disposition"] = "form-data; name=\"" + parameterName + "\"";
            _data = new MemoryStream(Encoding.UTF8.GetBytes(value));
        }

        public MimePart(string fieldName, string fileName, string contentType)
        {
            _headers["Content-Disposition"] = "form-data; name=\"" + fieldName + "\"; filename=\"" + fileName + "\"";
            _headers["Content-Type"] = StringLib.isValid(contentType) ? contentType : "application/octet-stream";
            _data = File.OpenRead(fileName);
        }

        public Hashtable Headers
        {
            get { return _headers; }
        }

        public byte[] Header
        {
            get { return _header; }
        }

        public long GenerateHeaderFooterData(string boundary)
        {
            StringBuilder sb = new StringBuilder();

            sb.Append("--");
            sb.Append(boundary);
            sb.AppendLine();
            foreach (string key in _headers.Keys)
            {
                sb.Append(key);
                sb.Append(": ");
                sb.AppendLine(_headers[key].ToString());
            }
            sb.AppendLine();

            _header = Encoding.UTF8.GetBytes(sb.ToString());

            return _header.Length + Data.Length + 2;
        }

        public Stream Data
        {
            get
            {
                return _data;
            }
        }
    }

    public class NetworkSecurity
    {


        private WebProxy proxy;
        public WebProxy Proxy
        {
            get
            {
                return proxy;
            }
        }

        private X509Certificate cert;
        public X509Certificate Certificate
        {
            get
            {
                return cert;
            }
        }
        public NetworkSecurity() { }

        public NetworkSecurity(string certificateFile)
        {
            if (File.Exists(certificateFile))
            {
                cert = X509Certificate.CreateFromCertFile(certificateFile);
            }
        }

        public NetworkSecurity(string host, int port, string domain, string username, string password)
        {
            proxy = new WebProxy(host, port);
            proxy.Credentials = new NetworkCredential(username, password, domain);
        }

        public NetworkSecurity(string host, int port)
        {
            proxy = new WebProxy(host, port);
            proxy.Credentials = CredentialCache.DefaultNetworkCredentials;
        }
    }
    #endregion
}
